//
//  QRCodeTool.m
//  二维码综合案例
//
//  Created by GR on 2017/10/23.
//  Copyright © 2017年 GR Harry. All rights reserved.
//

#import "QRCodeTool.h"
#import <AVFoundation/AVFoundation.h>

NSString * const QRCodeErrorDomain = @"cn.timeface.qrcodetool.errordomain";

@implementation QRCodeTool(Generator)

+ (UIImage *)generatorQRCodeWithContent:(NSString *)content imageScale:(CGFloat)imageScale correctionLevel:(QRCodeCorrectionLevel)correctionLevel centerImage:(UIImage *)centerImage centerImageSize:(CGSize)centerImageSize
{
    // 1. 生成滤镜
    CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    // 1.1 恢复默认
    [filter setDefaults];
    
    // 2. 设置输入
    NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
    [filter setValue:data forKey:@"InputMessage"];
    /*
     H : 30%
     Q : 25%
     M : 15%
     L : 7%
     */
    [filter setValue:[self p_correctionLevelString:correctionLevel] forKey:@"inputCorrectionLevel"];
    
    // 3. 取出图片
    CIImage *image = filter.outputImage;
    // 3.1 图片放大
    image = [image imageByApplyingTransform:CGAffineTransformMakeScale(imageScale, imageScale)];
    // 3.2 图片转换
    UIImage *resultImage = [UIImage imageWithCIImage:image];
    // 3.3 在图片正中间添加水印, 注意尺寸不要太大（最大不超过二维码图片的%30），太大会造成扫不出来
    if (centerImage) {
        resultImage = [self p_drawWaterImageWithOriginalImage:resultImage
                                                  centerImage:centerImage
                                              centerImageSize:centerImageSize];
    }
    return resultImage;
}

+ (UIImage *)generatorQRCodeWithContent:(NSString *)content imageScale:(CGFloat)imageScale
{
    return [self generatorQRCodeWithContent:content imageScale:imageScale correctionLevel:QRCodeCorrectionLevel_M centerImage:nil centerImageSize:CGSizeZero];
}

+ (UIImage *)generatorQRCodeWithContent:(NSString *)content
{
    return [self generatorQRCodeWithContent:content imageScale:20];
}

+ (UIImage *)generatorQRCodeWithContent:(NSString *)content centerImage:(UIImage *)centerImage centerImageSize:(CGSize)centerImageSize
{
    return [self generatorQRCodeWithContent:content imageScale:20 correctionLevel:QRCodeCorrectionLevel_M centerImage:centerImage centerImageSize:centerImageSize];
}

+ (UIImage *)generatorQRCodeWithContent:(NSString *)content centerImage:(UIImage *)centerImage
{
    UIImage *resultImage = [self generatorQRCodeWithContent:content];
    if (centerImage) {
        resultImage = [self p_drawWaterImageWithOriginalImage:resultImage
                                                  centerImage:centerImage
                                              centerImageSize:CGSizeMake(resultImage.size.width * 0.3f, resultImage.size.height * 0.3f)];
    }
    return resultImage;
}


#pragma mark - Private
+ (NSString *)p_correctionLevelString:(QRCodeCorrectionLevel)correctionLevel
{
    if (correctionLevel == QRCodeCorrectionLevel_H) {
        return @"H";
    }
    else if (correctionLevel == QRCodeCorrectionLevel_Q)  {
        return @"Q";
    }
    else if (correctionLevel == QRCodeCorrectionLevel_M)  {
        return @"M";
    }
    return @"L";
}
+ (UIImage *)p_drawWaterImageWithOriginalImage:(UIImage *)originalImage centerImage:(UIImage *)centerImage centerImageSize:(CGSize)centerImageSize
{
    CGSize size = originalImage.size;
    // 1. 开启图形上下文
    UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
    
    // 2. 绘制大图
    [originalImage drawInRect:CGRectMake(0,0 , size.width, size.height)];
    
    // 3. 绘制logo图片
    [centerImage drawInRect:CGRectMake((size.width - centerImageSize.width)/2.0, (size.height - centerImageSize.height)/2.0, centerImageSize.width, centerImageSize.height)];
    
    // 4. 取出图片
    UIImage *newPic = UIGraphicsGetImageFromCurrentImageContext();
    
    // 5. 关闭图形上下文
    UIGraphicsEndImageContext();
    
    // 6. 返回结果
    return newPic;
}
@end

@implementation QRCodeTool(Detector)

+ (void)detecteWithImage:(UIImage *)image isDrawQRCodeFrame:(BOOL)isDrawQRCodeFrame completion:(void (^)(NSArray<NSString *> *, UIImage *))completion
{
    __block UIImage *detectedImage = image;
    
    NSMutableArray <NSString *> *codeContents = [NSMutableArray arrayWithCapacity:0];
    
    [self p_detectorWithImage:image enumerateBlock:^(CIQRCodeFeature *feature) {
        /*
         feature.messageString: 二维码包含的信息.
         feature.bounds: 图片的位置
         */
        NSLog(@"message: %@ - bounce: %@",feature.messageString, NSStringFromCGRect(feature.bounds));
        // 4. 将二维码的位置信息绘制出来.
        [codeContents addObject:feature.messageString];
        
        if (isDrawQRCodeFrame) {
            detectedImage = [self p_drawQRCodeFrameWithSourceImage:detectedImage feature:feature];
        }
    }];
    
    if (completion) {
        completion(codeContents, detectedImage);
    } 
}

+ (NSArray <NSString *>*)detecteWithImage:(UIImage *)image
{
    NSMutableArray <NSString *> *codeContents = [NSMutableArray arrayWithCapacity:0];
    
    [self p_detectorWithImage:image enumerateBlock:^(CIQRCodeFeature *feature) {
        /*
         feature.messageString: 二维码包含的信息.
         feature.bounds: 图片的位置
         */
        [codeContents addObject:feature.messageString];
    }];
    
    return codeContents;
}

#pragma mark - Private
+ (void)p_detectorWithImage:(UIImage *)image enumerateBlock:(void (^)(CIQRCodeFeature *feature))enumerateBlock
{
    // 1. 获取待识别的图片
    UIImage *detectedImage = image;
    
    // 2. 创建探测器
    // CIDetectorAccuracy : 识别的精度
    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
    
    // 3. 探测图片
    CIImage *ciImage = [CIImage imageWithCGImage:detectedImage.CGImage];
    NSArray <CIQRCodeFeature *>* features = (NSArray <CIQRCodeFeature *>*)[detector featuresInImage:ciImage];
    for (CIQRCodeFeature *feature in features) {
        if (enumerateBlock) {
            enumerateBlock(feature);
        }
    }
}

+ (UIImage *)p_drawQRCodeFrameWithSourceImage:(UIImage *)sourceImage feature:(CIQRCodeFeature *)feature
{
    CGSize size = sourceImage.size;
    // 1. 开启图形上下文
    UIGraphicsBeginImageContext(size);
    
    // 2. 绘制原图
    [sourceImage drawInRect:(CGRect){CGPointZero, size}];
    
    // 3. 绘制边框
    // 3.1 翻转坐标系
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextScaleCTM(context, 1, -1);
    CGContextTranslateCTM(context, 0, -size.height);
    
    // 3.2 画边框
    CGRect bounce = feature.bounds;
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:bounce];
    [[UIColor redColor] set];
    path.lineWidth = 6.f;
    [path stroke];
    
    // 4. 获取图片
    UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
    
    // 5. 关闭上下文
    UIGraphicsEndImageContext();
    
    // 6. 返回结果
    return resultImage;
}
@end

@interface QRCodeTool()<AVCaptureMetadataOutputObjectsDelegate>
@property (strong, nonatomic) AVCaptureInput *input;
@property (strong, nonatomic) AVCaptureMetadataOutput *output;
@property (strong, nonatomic) AVCaptureSession *session;
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *layer;
@property (assign, nonatomic) BOOL isDrawFrame;
@property (weak, nonatomic)  id <QRCodeToolScanDelegate>scanDelegate;
@end
@implementation QRCodeTool

- (void)dealloc {
    if (self.session.isRunning) {
        [self.session stopRunning];
        [self.session removeInput:self.input];
        [self.session removeOutput:self.output];
    }
    NSLog(@"\n************\n %@ dealloc \n************", self.class);
}

@end

@implementation QRCodeTool(Scan)

- (void)scanQRCodeWithContainerView:(UIView *)containerView
                           delegate:(id<QRCodeToolScanDelegate>)delegate
{
    [self scanQRCodeWithContainerView:containerView isDrawFrame:NO delegate:delegate];
}

- (void)scanQRCodeWithContainerView:(UIView *)containerView isDrawFrame:(BOOL)isDrawFrame delegate:(id<QRCodeToolScanDelegate>)delegate;
{
    self.scanDelegate = delegate;
    self.isDrawFrame = isDrawFrame;
    
    [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
        if(granted){
            dispatch_async(dispatch_get_main_queue(), ^{
                // 3. 创建session连接输入和输出设备
                if ([self.session canAddInput:self.input] && [self.session canAddOutput:self.output]) {
                    [self.session addInput:self.input];
                    [self.session addOutput:self.output];
                }
                else {
                    NSError *error = [NSError errorWithDomain:QRCodeErrorDomain code:QRCodeErrorCodeSacnHardware userInfo:nil];
                    if (self.scanDelegate && [self.scanDelegate respondsToSelector:@selector(qrCodeTool:didEncountError:)]) {
                        [self.scanDelegate qrCodeTool:self didEncountError:error];
                    }
                    return;
                }
                
                // 3.1 设置二维码可以识别的码制
                // 这句代码必须在output添加入session之后设置，不然崩溃
                self.output.metadataObjectTypes = @[ AVMetadataObjectTypeQRCode ];
                
                // 3.2 添加视频预览图层, 不加入该图层，也能扫描二维码。
                if (![containerView.layer.sublayers containsObject:self.layer]) {
                    self.layer.frame = containerView.bounds;
                    [containerView.layer insertSublayer:self.layer atIndex:0];
                }
            });
        }
        else {
            NSError *error = [NSError errorWithDomain:QRCodeErrorDomain code:QRCodeErrorCodeScanNoAuthorization userInfo:nil];
            if (self.scanDelegate && [self.scanDelegate respondsToSelector:@selector(qrCodeTool:didEncountError:)]) {
                [self.scanDelegate qrCodeTool:self didEncountError:error];
            }
        }
    }];
    
    
}

- (void)startScanning {
    [self.session startRunning];
}

- (void)stopScanning {
    [self.session stopRunning];
}

- (void)setRectOfInterest:(CGRect)rect
{
    // 2.2 设置敏感区域.
    // rectOfInterest是个比例值,x, y, width, height 都在(0~1)之间
    // rectOfInterest是以右上角为原点.
    CGSize screenSize = [UIScreen mainScreen].bounds.size;
    CGFloat x = rect.origin.y / screenSize.height;
    CGFloat y = rect.origin.x / screenSize.width;
    CGFloat width = rect.size.height / screenSize.height;
    CGFloat height = rect.size.width / screenSize.width;
    self.output.rectOfInterest = CGRectMake(x, y, width, height);
    
    // 可以直接使用这个方法转换，但是方法依赖与self.layer,由于layer的创建前需要先获取权限，获取权限的过程是异步的，调用到这里的时候layer还没有创建赋值frame，所以这里用下面的方法获取的结果不对。在目前设定的需求中，使用此方法不行，故采用上面的方法。
//    self.output.rectOfInterest = [self.layer metadataOutputRectOfInterestForRect:rect];
}

#pragma mark - Getter
- (AVCaptureInput *)input
{
    if (!_input) {
        // 1. 设置输入
        // 1.1 获取摄像头
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        // 1.2 设置摄像头为输入设备
        NSError *error = nil;
        _input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
        if (error) {
            return nil;
        }
    }
    return _input;
}

- (AVCaptureMetadataOutput *)output
{
    if (!_output) {
        // 2. 设置输出
        _output = [[AVCaptureMetadataOutput alloc] init];
        // 2.1 设置代理
        [_output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    }
    return _output;
}

- (AVCaptureSession *)session
{
    if (!_session) {
        _session = [[AVCaptureSession alloc] init];
    }
    return _session;
}

- (AVCaptureVideoPreviewLayer *)layer
{
    if (!_layer) {
        _layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    }
    return _layer;
}

#pragma mark - AVCaptureMetadataOutputObjectsDelegate
- (void)captureOutput:(AVCaptureOutput *)output didOutputMetadataObjects:(NSArray<__kindof AVMetadataObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    if (self.isDrawFrame) {
        [self p_removeAllFrame];
    }
    
    NSMutableArray <NSString *>* codeContents = [NSMutableArray arrayWithCapacity:0];
    for (int i = 0; i < metadataObjects.count; i++) {
        AVMetadataMachineReadableCodeObject *resultObj = (AVMetadataMachineReadableCodeObject *)[self.layer transformedMetadataObjectForMetadataObject:metadataObjects[i]];
        NSLog(@"code: %@", resultObj.stringValue);
        [codeContents addObject:resultObj.stringValue];
        
        if (self.isDrawFrame) {
            [self p_drawFrameWithMetadataObject:resultObj];
        }
    }
    
    if (self.scanDelegate && [self.scanDelegate respondsToSelector:@selector(qrCodeTool:didScanResult:)]) {
        [self.scanDelegate qrCodeTool:self didScanResult:codeContents];
    }
}

#pragma mark - Private
- (void)p_drawFrameWithMetadataObject:(AVMetadataMachineReadableCodeObject *)metadataObject
{
    // 1. 创建CAShapeLayer
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    shapeLayer.strokeColor = [UIColor redColor].CGColor;
    shapeLayer.lineWidth = 3.f;
    
    // 2. 根据4个点绘制路径
    UIBezierPath *path = [UIBezierPath bezierPath];
    for (int i = 0; i < metadataObject.corners.count; i++) {
        CFDictionaryRef dicRef = ((__bridge CFDictionaryRef) metadataObject.corners[i]);
        CGPoint point;
        CGPointMakeWithDictionaryRepresentation(dicRef, &point);
        
        if (i == 0) {
            [path moveToPoint:point];
        }
        else {
            [path addLineToPoint:point];
        }
    }
    [path closePath];
    
    // 3. 给图层赋值路径
    shapeLayer.path = path.CGPath;
    
    // 4. 添加图层
    [self.layer addSublayer:shapeLayer];
}

- (void)p_removeAllFrame
{
    NSArray *sublayers = [NSArray arrayWithArray:self.layer.sublayers];
    
    for (CALayer *layer in sublayers) {
        if ([layer isKindOfClass:[CAShapeLayer class]]) {
            [layer removeFromSuperlayer];
        }
    }
}
@end
